/**
 * Created by wust on 2020-04-16 10:27:56
 * Copyright © 2020 wust. All rights reserved.
 */
package com.sc.common;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ClassLoaderUtil;
import cn.hutool.core.util.ReflectUtil;
import com.alibaba.fastjson.JSONObject;
import com.sc.common.annotations.EnableDistributedCaching;
import com.sc.common.annotations.EnableLocalCaching;
import com.sc.common.cache.CacheAbstract;
import com.sc.common.enums.EhcacheKeyEnum;
import com.sc.common.enums.RedisKeyEnum;
import com.sc.common.exception.BusinessException;
import com.sc.common.service.InitializtionService;
import com.sc.common.util.MyStringUtils;
import com.sc.common.util.SpringContextHolder;
import com.sc.common.util.cache.EhcacheTools;
import com.sc.common.util.cache.SpringRedisTools;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.aop.support.AopUtils;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author: wust
 * @date: Created in 2020-04-16 10:27:56
 * @description:
 *
 */
public abstract class BaseWebInitializer {
    static Logger logger = LogManager.getLogger(BaseWebInitializer.class);

    protected abstract String getEntityPath();

    protected abstract String[] getCacheNames();

    protected void resetCached(ConfigurableApplicationContext configurableApplicationContext){
        EhcacheTools ehcacheTools = SpringContextHolder.getBean("ehcacheTools");
        String[] cacheNames = getCacheNames();
        if(cacheNames != null && cacheNames.length > 0){
            for (String cacheName : cacheNames) {
                ehcacheTools.clear(cacheName);
            }
        }
    }

    /**
     * 执行实现了InitDataService接口的init方法
     * @param configurableApplicationContext
     */
    protected void intHandle(ConfigurableApplicationContext configurableApplicationContext){
        Object beansOfType1 = configurableApplicationContext.getBeansOfType(InitializtionService.class);
        if (beansOfType1 != null) {
            TreeMap<Integer, List<InitializtionService>> treeMap = new TreeMap<>();
            Map<String, Object> dataServiceMap = (Map<String, Object>) beansOfType1;
            Set<Map.Entry<String, Object>> entrySet = dataServiceMap.entrySet();
            for (Map.Entry<String, Object> entry : entrySet) {
                if (entry == null || entry.getValue() == null) {
                    continue;
                }

                Object obj = entry.getValue();
                Order orderAnnotation = AopUtils.getTargetClass(obj).getAnnotation(Order.class);
                if (orderAnnotation == null) {
                    throw new BusinessException("初始化类必须指定初始化顺序，请为该类添加org.springframework.core.annotation.Order(int)注解");
                } else {
                    int order = orderAnnotation.value();
                    if (treeMap.containsKey(order)) {
                        List<InitializtionService> list = treeMap.get(order);
                        list.add((InitializtionService) obj);
                        treeMap.put(order, list);
                    } else {
                        List<InitializtionService> list = new ArrayList<>();
                        list.add((InitializtionService) obj);
                        treeMap.put(order, list);
                    }
                }
            }

            Set<Map.Entry<Integer, List<InitializtionService>>> entrySet1 = treeMap.entrySet();
            for (Map.Entry<Integer, List<InitializtionService>> entry : entrySet1) {
                List<InitializtionService> list = entry.getValue();
                if (CollectionUtil.isNotEmpty(list)) {
                    for (InitializtionService initDataService : list) {
                        initDataService.init();
                    }
                }
            }
        }
    }


    /**
     * 处理复杂缓存
     * @param configurableApplicationContext
     */
    protected void complexCachingHandle(ConfigurableApplicationContext configurableApplicationContext){
        Object beansOfType2 = configurableApplicationContext.getBeansOfType(CacheAbstract.class);
        if (beansOfType2 != null) {
            Map<String, CacheAbstract> cacheAbstractMap = (Map<String, CacheAbstract>) beansOfType2;
            Set<Map.Entry<String, CacheAbstract>> entrySet = cacheAbstractMap.entrySet();
            for (Map.Entry<String, CacheAbstract> entry : entrySet) {
                if (entry == null || entry.getValue() == null) {
                    continue;
                }

                CacheAbstract redisCacheAbstract = entry.getValue();
                redisCacheAbstract.init();
            }
        }
    }


    /**
     * 本地缓存，系统启动初始化本地缓存。此处只初始化主键=持久化对象数据
     *
     * @param configurableApplicationContext
     */
    protected void localCachingHandle(ConfigurableApplicationContext configurableApplicationContext) {
        if(MyStringUtils.isBlank(MyStringUtils.null2String(getEntityPath()))){
            return;
        }

        EhcacheTools ehcacheTools = SpringContextHolder.getBean("ehcacheTools");

        List<Class> classList = new ArrayList<>(20);
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        MetadataReaderFactory metaReader = new CachingMetadataReaderFactory();
        Resource[] resources = null;
        try {
            resources = resolver.getResources(getEntityPath());
            for (Resource resource : resources) {
                MetadataReader reader = metaReader.getMetadataReader(resource);
                String className = reader.getClassMetadata().getClassName();
                Class<?> clazz = ClassLoaderUtil.loadClass(className.replace("BOOT-INF.classes",""));
                EnableLocalCaching enableLocalCaching = clazz.getAnnotation(EnableLocalCaching.class);
                if(enableLocalCaching != null){
                    classList.add(clazz);
                }
            }
        } catch (IOException e) {
            logger.error("系统启动时处理本地缓存失败：",e);
        }


        if(CollectionUtil.isNotEmpty(classList)){
            for (Class aClass : classList) {
                String source = aClass.getSimpleName();
                String beanName = source.substring(0, 1).toLowerCase() + source.substring(1) + "Mapper";
                Object bean = SpringContextHolder.getBean(beanName);
                try {
                    Method mtd = bean.getClass().getMethod("selectAll");
                    Object listObj = mtd.invoke(bean);
                    if (listObj != null) {
                        List list = (List) listObj;
                        for (Object o : list) {
                            if (o == null) {
                                continue;
                            }

                            Object primaryKey = ReflectUtil.getFieldValue(o, "id");
                            if (primaryKey == null) {
                                continue;
                            }

                            String localCachedKey = String.format(EhcacheKeyEnum.EHCACHE_KEY_SELECT_BY_PRIMARY_KEY.getStringValue(), source, Convert.toStr(primaryKey));
                            ehcacheTools.put("poCache",localCachedKey,o);
                        }
                    }
                } catch (NoSuchMethodException e) {
                    logger.error("系统启动时处理本地缓存失败，找不到方法异常：",e);
                } catch (IllegalAccessException e) {
                    logger.error("系统启动时处理本地缓存失败",e);
                } catch (InvocationTargetException e) {
                    logger.error("系统启动时处理本地缓存失败",e);
                }
            }
        }
    }

    /**
     * 分布式缓存，系统启动初始化分布式缓存。此处只缓存主键=持久化对象的缓存
     *
     * @param configurableApplicationContext
     */
    protected void distributedCachingHandle(ConfigurableApplicationContext configurableApplicationContext) {
        if(MyStringUtils.isBlank(MyStringUtils.null2String(getEntityPath()))){
            return;
        }

        SpringRedisTools springRedisTools = configurableApplicationContext.getBean(SpringRedisTools.class);

        List<Class> classList = new ArrayList<>(20);
        ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        MetadataReaderFactory metaReader = new CachingMetadataReaderFactory();
        Resource[] resources = null;
        try {
            resources = resolver.getResources(getEntityPath());
            for (Resource resource : resources) {
                MetadataReader reader = metaReader.getMetadataReader(resource);
                String className = reader.getClassMetadata().getClassName();
                Class<?> clazz = ClassLoaderUtil.loadClass(className.replace("BOOT-INF.classes",""));
                EnableDistributedCaching enableDistributedCaching = clazz.getAnnotation(EnableDistributedCaching.class);
                if(enableDistributedCaching != null){
                    classList.add(clazz);
                }
            }
        } catch (IOException e) {
            logger.error("系统启动时处理分布式缓存失败：",e);
        }


        if(CollectionUtil.isNotEmpty(classList)){
            for (Class aClass : classList) {
                String source = aClass.getSimpleName();
                String beanName = source.substring(0, 1).toLowerCase() + source.substring(1) + "Mapper";
                Object bean = SpringContextHolder.getBean(beanName);
                try {
                    Method mtd = bean.getClass().getMethod("selectAll");
                    Object listObj = mtd.invoke(bean);
                    if (listObj != null) {
                        List list = (List) listObj;
                        for (Object o : list) {
                            if (o == null) {
                                continue;
                            }

                            Object primaryKey = ReflectUtil.getFieldValue(o, "id");
                            if (primaryKey == null) {
                                continue;
                            }

                            String redisKey = String.format(RedisKeyEnum.REDIS_KEY_PRIMARY_SELECT_BY_PRIMARY_KEY.getStringValue(), source, Convert.toStr(primaryKey));
                            if (springRedisTools.hasKey(redisKey)) {
                                springRedisTools.deleteByKey(redisKey);
                            }
                            springRedisTools.addData(redisKey, JSONObject.toJSONString(o));
                        }
                    }
                } catch (NoSuchMethodException e) {
                    logger.error("系统启动时处理分布式缓存失败，找不到方法异常：",e);
                } catch (IllegalAccessException e) {
                    logger.error("系统启动时处理分布式缓存失败：",e);
                } catch (InvocationTargetException e) {
                    logger.error("系统启动时处理分布式缓存失败：",e);
                }
            }
        }
    }
}
